package com.lam.common.validate;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;

import org.hibernate.validator.HibernateValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.ser.std.StdKeySerializers.Default;
import com.lam.common.exception.ParameterErrorException;
import com.lam.common.utils.BeanUtil;
import com.lam.common.utils.CollectionUtil;
import com.lam.common.utils.JsonUtil;
import com.lam.common.utils.SpringUtils;
import com.lam.common.utils.StringUtils;
import com.lam.common.validate.constant.ValidConstant;
import com.lam.common.validate.model.UniqueDesc;
import com.lam.common.validate.model.ValidOperType;
import com.lam.common.validate.service.ValidateService;
import com.lam.common.validate.validator.DataUsedValidBean;
import com.lam.common.validate.validator.DictValidBean;
import com.lam.common.validate.validator.UniqueValidBean;

public class ValidateUtil {
	
	private static final Logger logger = LoggerFactory.getLogger(ValidateUtil.class);
	
	public static ValidateService getValidateService() {
		return SpringUtils.getBean(ValidateService.class);
	}
	
	/**
	 * 根据tableUniqueCode查询唯一配置
	 * @param tableUniqueCode
	 * @return
	 */
	public static UniqueDesc selectUniqueDesc(String tableUniqueCode) {
		return getValidateService().selectUniqueByCode(tableUniqueCode);
	}
	/**
	 * 根据唯一配置uniqueDesc查询数据
	 * @param uniqueDesc
	 * @return
	 */
	public static Map<String, Object> selectData(UniqueDesc uniqueDesc) {
		return getValidateService().selectUniqueData(uniqueDesc);
	}
	
	/**
	 * 校验对象的属性是否必填
	 * @param obj 对象
	 * @param fieldName 属性名
	 * @param errorMsg 错误提示
	 * @param throwExp 校验不通过是否抛出异常， true抛出异常,false不抛异常 
	 * @return
	 */
	public static String validRequired(Object obj, String fieldName, String errorMsg) {
		return validRequired(obj, fieldName, errorMsg, true);
	}
	
	/**
	 * 校验对象的属性是否必填
	 * @param obj 对象
	 * @param fieldName 属性名
	 * @param errorMsg 错误提示
	 * @param throwExp 校验不通过是否抛出异常， true抛出异常,false不抛异常 
	 * @return
	 */
	public static String validRequired(Object obj, String fieldName, String errorMsg, boolean throwExp) {
		
		Object val;
		try {
			val = BeanUtil.getValueByName(obj, fieldName);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			val = null;
		}
		return validRequired(val, errorMsg, throwExp);
	}
	
	/**
	 * 校验obj是否为空, 校验不通过时抛出异常
	 * @param obj 值
	 * @param errorMsg 错误提示
	 * @return
	 */
	public static String validRequired(Object obj, String errorMsg) {
		return validRequired(obj, errorMsg, true);
	}
	
	/**
	 * 校验obj是否为空
	 * @param obj 值
	 * @param errorMsg 错误提示
	 * @param throwExp 校验不通过是否抛出异常， true抛出异常,false不抛异常
	 * @return
	 */
	public static String validRequired(Object obj, String errorMsg, boolean throwExp) {
		
		boolean isBlank = false;
		if(obj == null) {
			isBlank = true;
		}else if(obj instanceof String) {
			isBlank = StringUtils.isBlank(obj.toString());
		}else if(CollectionUtil.isArray(obj)){
			isBlank = ((Object[])obj).length==0;
		}else if(obj instanceof Collection<?>){
			isBlank = ((Collection<?>)obj).size()==0;
		}else if(obj instanceof Map<?, ?>){
			isBlank = ((Map<?, ?>)obj).size()==0;
		}
		if(isBlank) {
			if(throwExp) {
				throw new ParameterErrorException(errorMsg);
			}
			return errorMsg;
		}
		return null;
	}
	
	public static String valid(String value, String regex, String errorMsg) {
		return valid(value, regex, errorMsg, true);
	}
	/**
	 * 使用正则表达式进行验证
	 * @param value
	 * @param regex
	 * @param errorMsg
	 * @param throwExp
	 * @return
	 */
	public static String valid(String value, String regex, String errorMsg, boolean throwExp) {
		if(StringUtils.isBlank(value)) {
			return null;
		}
		if(!value.matches(regex)) {
			if(throwExp) {
				throw new ParameterErrorException(errorMsg);
			}
			return errorMsg;
		}
		return null;
	}
	
	// ====== HibernateValidator校验工具 start =========== 
	
//	private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
	private static final Validator validator = Validation.byProvider(HibernateValidator.class).configure().failFast(true).buildValidatorFactory().getValidator();
	
	/**
	 * 根据校验分组对object对象进行验证，校验不通过则抛出ParameterErrorException异常
	 * @param object 待校验对象
	 * @param groups 校验分组
	 */
	public static void validateWithExp(Object object, Class<?> ... groups) {
		String msg = validate(object, groups);
		if(StringUtils.isNotBlank(msg)) {
			throw new ParameterErrorException(msg);
		}
	}

	/**
	 * 根据校验分组对object对象进行验证
	 * @param object 待校验对象
	 * @param groups 校验分组
	 * @return 校验不通过则返回错误信息，返回null表示校验通过
	 */
	public static String validate(Object object, Class<?> ... groups) {
		
		if(CollectionUtil.isEmpty(groups)) {
			groups = new Class<?>[]{Default.class};
		}
		
		Set<ConstraintViolation<Object>> validResult = validator.validate(object, groups);
		if(CollectionUtil.isEmpty(validResult)) {
			return null;
		}
		StringBuffer message = new StringBuffer();
		for (ConstraintViolation<Object> validRs : validResult) {
//			message.append(message.length()<1 ? "" : ", ").append(validRs.getPropertyPath().toString()).append(" ").append(validRs.getMessage());
			message.append(message.length()<1 ? "" : ", ").append(validRs.getMessage());
		}
		return message.toString();
	}
	// ====== HibernateValidator校验工具 end =========== 
	
	// ====== 字典有效值校验 start =========== 
	
	/**
	 * 根据校验分组对object对象中的字典字段值的有效性进行验证，校验不通过则抛出ParameterErrorException异常
	 * @param object 待校验对象
	 * @param groups 校验分组
	 */
	public static void validDictWithExp(Object object, Class<?> ... groups) {
		String msg = validDict(object, groups);
		if(StringUtils.isNotBlank(msg)) {
			throw new ParameterErrorException(msg);
		}
	}
	
	/**
	 * 根据校验分组对object对象中的字典字段值的有效性进行验证
	 * @param object 待校验对象
	 * @param groups 校验分组
	 * @return 校验不通过则返回错误信息，返回null表示校验通过
	 */
	public static String validDict(Object object, Class<?> ... groups) {
		if(object == null) {
			return null;
		}
		if(CollectionUtil.isEmpty(groups)) {
			groups = new Class<?>[]{Default.class};
		}
		return DictValidBean.newInstance(object.getClass(), groups).check(object);
	}
	// ====== 字典有效值校验 end =========== 
	
	// ====== 表字段唯一校验 start =========== 
	
	public static String validUnique(Object data) {
		
		return data == null ? ValidConstant.UNIQUE_YES : ValidConstant.UNIQUE_NO;
	}
	
	public static void validUniqueWithExp(Object object, String errorMsg) {
		if(object != null) {
			throw new ParameterErrorException(errorMsg);
		}
	}
	
	/**
	 * 根据tableUniqueCode对该实体对象的某个字段的唯一性进行校验，校验不通过则抛出ParameterErrorException异常
	 * @param object 待校验对象
	 * @param operType 编辑类型
	 * @param tableUniqueCode 唯一配置编码
	 * @param errorMsg 错误提示语
	 */
	public static void validUnique(Object object, ValidOperType operType, String tableUniqueCode, String errorMsg) {
		if(ValidConstant.UNIQUE_NO.equals(validUnique(object, operType, tableUniqueCode))) {
			throw new ParameterErrorException(errorMsg);
		}
	}
	
	/**
	 * 根据tableUniqueCode对该实体对象的某个字段的唯一性进行校验
	 * @param object 待校验对象
	 * @param operType 编辑类型
	 * @param tableUniqueCode 唯一配置编码
	 * @return "false"-校验不通过，"true"表示校验通过
	 */
	public static String validUnique(Object object, ValidOperType operType, String tableUniqueCode) {
		
		validRequired(tableUniqueCode, "唯一配置编码不能为空！");
		UniqueDesc uniqueDesc = selectUniqueDesc(tableUniqueCode);
		validRequired(uniqueDesc, "不存在的唯一配置code，"+tableUniqueCode);
		
		return validUnique(object, operType, uniqueDesc);
	}
	
	/**
	 * 根据uniqueDesc对该实体对象的某个字段的唯一性进行校验
	 * @param object 待校验对象
	 * @param operType 编辑类型
	 * @param uniqueDesc 唯一配置
	 * @return "false"-校验不通过，"true"表示校验通过
	 */
	public static String validUnique(Object object, ValidOperType operType, UniqueDesc uniqueDesc) {
		
		return UniqueValidBean.checkUnique(object, operType, uniqueDesc);
	}
	
	/**
	 * 根据唯一配置uniqueDesc查询数据，并转换成对应的实体对象
	 * @param <T>
	 * @param object
	 * @param operType
	 * @param uniqueDesc
	 * @param clazz
	 * @return
	 */
	public static <T> T selectUniqueData(Object object, ValidOperType operType, UniqueDesc uniqueDesc, Class<T> clazz) {
		return JsonUtil.toJavaObjectWithSnake(UniqueValidBean.selectUniqueData(object, ValidOperType.ADD, uniqueDesc), clazz);
	}
	// ====== 表字段唯一校验 end =========== 
	
	// ====== 数据被使用校验 start =========== 
	/**
	 * 判断数据是否被指定表记录使用，例:查询某角色id是否被用户角色关联表(sys_user_role)使用，被使用则抛出 BaseException 异常
	 * @param tableName 表名
	 * @param selectField 查询列
	 * @param object <表字段, 待校验数据>
	 * @param errorMsg 错误提示语
	 */
	public static void validDataUsed(String tableName, String selectField, Map<String, Object> fieldMap, String errorMsg) {
		DataUsedValidBean.validDataUsed(tableName, selectField, fieldMap, errorMsg);
	}
	
	/**
	 * 判断数据是否被指定表记录使用，例:查询某角色id是否被用户角色关联表(sys_user_role)使用，被使用则抛出 BaseException 异常
	 * @param tableName 表名
	 * @param column 表字段
	 * @param object 待校验数据
	 * @param errorMsg 错误提示语
	 */
	public static void validDataUsed(String tableName, String column, Object object, String errorMsg) {
		DataUsedValidBean.validDataUsed(tableName, column, object, errorMsg);
	}
	/**
	 * 判断数据是否被指定表记录使用，例:查询某角色id是否被用户角色关联表(sys_user_role)使用
	 * @param tableName 表名
	 * @param selectField 查询列
	 * @param fieldMap <表字段, 待校验数据>
	 * @return true表示被使用，false表示未被使用
	 */
	public static boolean validDataUsed(String tableName, String selectField, Map<String, Object> fieldMap) {
		return DataUsedValidBean.validDataUsed(tableName, selectField, fieldMap);
	}
	
	/**
	 * 判断数据是否被指定表记录使用，例:查询某角色id是否被用户角色关联表(sys_user_role)使用
	 * @param tableName 表名
	 * @param column 表字段
	 * @param object 待校验数据
	 * @return true表示被使用，false表示未被使用
	 */
	public static boolean validDataUsed(String tableName, String column, Object object) {
		return DataUsedValidBean.validDataUsed(tableName, column, object);
	}
	// ====== 数据被使用校验 end =========== 
	
	
}
